package com.ryan.framework.dao.impl;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.ryan.framework.dao.BaseMybatisDao;
import com.ryan.framework.model.Model;
import com.ryan.framework.model.PagedQueryObject;
import com.ryan.framework.model.QueryObject;
import com.ryan.framework.model.UpdateByQueryObject;
import com.ryan.framework.model.QueryObject.Criteria;
import com.ryan.framework.model.QueryObject.CriteriaParameter;
import com.ryan.framework.model.QueryObject.OrderBy;

/**
 * This class serves as the Base class for all other DAOs - namely to hold
 * common CRUD methods that they might all use. You should only need to extend
 * this class when your require custom CRUD logic.
 * 
 * <p>
 * To register this class in your Spring context file, use the following XML.
 * 
 * <pre>
 *      &lt;bean id=&quot;fooDao&quot; class=&quot;com.forbug.framework.dao.hibernate.GenericDaoHibernate&quot;&gt;
 *          &lt;constructor-arg value=&quot;com.forbug.framework.model.Foo&quot;/&gt;
 *          &lt;property name=&quot;sessionFactory&quot; ref=&quot;sessionFactory&quot;/&gt;
 *      &lt;/bean&gt;
 * </pre>
 * 
 * @author cheney.yuan
 * @param <M>
 *            a type variable
 * @param <PK>
 *            the primary key for that type
 */
public abstract class BaseMybatisDaoSupport<M extends Model, PK extends Serializable> extends
		SqlMapClientDaoSupport implements BaseMybatisDao<M, PK> {
	/**
	 * Log variable for all child classes. Uses LogFactory.getLog(getClass())
	 * from Commons Logging
	 */
	protected final Logger logger = Logger.getLogger(this.getClass());
	
	/**
	 * 持久化类
	 */
	private Class<M> persistentClass;
	
	/**
	 * 获得属性和数据库字段的映射集合 Map的key为属性，value为数据库字段
	 * 
	 * @return
	 */
	protected abstract Map<String, String> getPropertityMappingMap();

	/**
	 * Constructor that takes in a class to see which type of entity to persist
	 * 
	 * @param persistentClass
	 *            the class type you'd like to persist
	 */
	public BaseMybatisDaoSupport(final Class<M> persistentClass) {
		this.persistentClass = persistentClass;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<M> findAll() {
		return super.getSqlMapClientTemplate().queryForList(persistentClass.getSimpleName() + "." + "findAll");
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public M findByPrimaryKey(PK id) {
		M entity = (M) super.getSqlMapClientTemplate().queryForObject(persistentClass.getSimpleName() + "."+"findByPrimaryKeyClass", id);
		if (entity == null) {
			logger.warn("Uh oh, '" + this.persistentClass + "' object with id '"
					+ id + "' not found...");
			throw new ObjectRetrievalFailureException(this.persistentClass, id);
		}
		return entity;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public boolean exists(PK id) {
		M entity = (M) super.getSqlMapClientTemplate().queryForObject(persistentClass.getSimpleName() + "."+ "findByPrimaryKeyClass", id);
		return entity != null;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public PK insert(M object) {
		return (PK) super.getSqlMapClientTemplate().insert(persistentClass.getSimpleName() + "." + "insert", object);
	}

	/**
	 * {@inheritDoc}
	 */	
	public void update(M object) {
		super.getSqlMapClientTemplate().update(persistentClass.getSimpleName() + "." + "update", object);
	}

	/**
	 * {@inheritDoc}
	 */
	public void remove(PK id) {
		super.getSqlMapClientTemplate().delete(persistentClass.getSimpleName() + "." + "delete", id);
	}

	/**
	 * {@inheritDoc}
	 */	
	public List<M> findByNamedQuery(String queryName,Map<String, Object> queryParams) {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.j2ee.jpa.dao.GenericDao#count(java.util.Map)
	 */
	public long getResultCount(QueryObject queryObject) {
		propertityToColumn(queryObject);
		Long count = (Long) super.getSqlMapClientTemplate().queryForObject(persistentClass.getSimpleName() + "." + "getResultCount",queryObject);
		return count;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.j2ee.jpa.dao.GenericDao#findByParametersQuery(java.util.Map,
	 *      java.lang.String, boolean, int, int)
	 */
	public void updateByQueryObject(UpdateByQueryObject<M> queryObject) {
		propertityToColumn(queryObject);
		super.getSqlMapClientTemplate().update(
				persistentClass.getSimpleName() + "." + "updateByQueryObject",
				queryObject);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.j2ee.jpa.dao.GenericDao#findByParametersQuery(java.util.Map,
	 *      java.lang.String, boolean, int, int)
	 */
	public void deleteByQueryObject(QueryObject queryObject) {
		propertityToColumn(queryObject);
		super.getSqlMapClientTemplate().delete(
				persistentClass.getSimpleName() + "." + "deleteByQueryObject",
				queryObject);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.j2ee.jpa.dao.GenericDao#findByParametersQuery(java.util.Map,
	 *      java.lang.String, boolean, int, int)
	 */
	@SuppressWarnings("unchecked")
	public List<M> findByPagedQueryObject(PagedQueryObject pagedQueryObject) {
		propertityToColumn(pagedQueryObject);
		List<M> resuntList = (List<M>) super.getSqlMapClientTemplate()
				.queryForList(
						persistentClass.getSimpleName() + "."
								+ "findByPagedQueryObject", pagedQueryObject);
		return resuntList;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.j2ee.jpa.dao.GenericDao#findByParametersQuery(java.util.Map,
	 *      java.lang.String, boolean)
	 */
	@SuppressWarnings("unchecked")
	public List<M> findByQueryObject(QueryObject queryObject) {
		propertityToColumn(queryObject);
		List<M> resuntList = (List<M>) super.getSqlMapClientTemplate()
				.queryForList(
						persistentClass.getSimpleName() + "."
								+ "findByQueryObject", queryObject);
		return resuntList;
	}

	/**
	 * 处理queryObject
	 * 
	 * @param hql
	 * @param queryObject
	 * @return
	 */
	private void propertityToColumn(QueryObject queryObject) {
		Map<String, String> mappingMap = this.getPropertityMappingMap();
		if (mappingMap == null || mappingMap.size() == 0) {
			return;
		}

		if (queryObject != null) {
			List<Criteria> orCriteriaList = queryObject.getOredCriteriaList();
			for (Criteria cri : orCriteriaList) {
				// 无参数的查询条件
				List<CriteriaParameter> withoutValueList = cri.getCriteriaWithoutValue();
				for (CriteriaParameter parameter : withoutValueList) {
					String column = parameter.getPropertity();
					if (mappingMap.containsKey(parameter.getPropertity())) {
						column = mappingMap.get(parameter.getPropertity());
					}
					parameter.setColumn(column);
				}
				// 单值参数的查询条件
				List<CriteriaParameter> singleValueList = cri.getCriteriaWithSingleValue();
				for (CriteriaParameter parameter : singleValueList) {
					String column = parameter.getPropertity();
					if (mappingMap.containsKey(parameter.getPropertity())) {
						column = mappingMap.get(parameter.getPropertity());
					}
					parameter.setColumn(column);
				}
				// 多值参数的查询条件
				List<CriteriaParameter> listValueList = cri
						.getCriteriaWithListValue();
				for (CriteriaParameter parameter : listValueList) {
					String column = parameter.getPropertity();
					if (mappingMap.containsKey(parameter.getPropertity())) {
						column = mappingMap.get(parameter.getPropertity());
					}
					parameter.setColumn(column);
				}
				// betweenAnd的查询条件
				List<CriteriaParameter> betweenAndValueList = cri.getCriteriaWithBetweenValue();
				for (CriteriaParameter parameter : betweenAndValueList) {
					String column = parameter.getPropertity();
					if (mappingMap.containsKey(parameter.getPropertity())) {
						column = mappingMap.get(parameter.getPropertity());
					}
					parameter.setColumn(column);
				}
			}
			// 处理orderBy
			List<OrderBy> orderByList = queryObject.getOderByList();
			for (OrderBy orderBy : orderByList) {
				String column = orderBy.getPropertity();
				if (mappingMap.containsKey(orderBy.getPropertity())) {
					column = mappingMap.get(orderBy.getPropertity());
				}
				orderBy.setColumn(column);
			}
		}
	}

}
